/**
 *
 * The MIT License
 *
 * Copyright (c) 2008 Board of Trustees, Leland Stanford Jr. University. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.macdadi.ks.client.components;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.*;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * @author Ben Suter <ben.suter@gmail.com>
 */
public class EditableTree extends Tree implements TreeListener
{
    protected TreeItem currentSelectedItem;
    protected Element editorParent, tempEditor;
    protected ArrayList availableItemEditors = new ArrayList();
    protected ArrayList currentItemEditors = new ArrayList();

    public EditableTree()
    {
        super();
        addTreeListener(this);
    }

    public EditableTree(TreeImages images)
    {
        super(images);
        addTreeListener(this);
    }

    /**
     * An empty implementation, we only respond to the selection events
     * @param item the item whose state changed
     */
    public void onTreeItemStateChanged(TreeItem item) {
        // do nothing
    }

    /**
     * Make this currently selected item editable
     * @param item the selected item
     */
    public void onTreeItemSelected(TreeItem item)
    {
        if ( currentSelectedItem != null )
            returnItemToNonEditableState();

        currentSelectedItem = item;

        makeItemEditable(currentSelectedItem);
    }
    
    /**
     * Return the item, if any, currently being edited to its
     * non-editable state.
     */
    protected void returnItemToNonEditableState()
    {
        for (Iterator it = currentItemEditors.iterator (); it.hasNext (); ) {
            ItemEditor ie = (ItemEditor) it.next();
            ie.stopEditing();
            RootPanel.get().remove(ie.getWidget());
        }

        currentItemEditors.clear();

        if ( editorParent != null && tempEditor != null )
            DOM.removeChild(editorParent, tempEditor);
        
        tempEditor = null;
        editorParent = null;
    }

    /**
     * Display in-line editor controls as part of the specified item
     * @param item the TreeItem to be made editable
     */
    protected void makeItemEditable(TreeItem item)
    {
        Element itemDiv = item.getElement(); // the main div
        Element table = DOM.getChild(itemDiv, 0);
        Element tbody = DOM.getChild(table, 0);
        Element tr = DOM.getChild(tbody, 0);
        Element tdEdit = DOM.createTD();
        DOM.appendChild(tr, tdEdit);
        DOM.setStyleAttribute(tdEdit, "verticalAlign", "middle");

        editorParent = tr;
        tempEditor = tdEdit;

        for (Iterator it = availableItemEditors.iterator(); it.hasNext();) {
            ItemEditor ie = (ItemEditor) it.next();
            if ( ie.matchesItem(item) ) {
                ie.startEditing(item);
                RootPanel.get().add(ie.getWidget());
                // if this doesn't work, use getTempElement again
                DOM.appendChild(tempEditor, ie.getWidget().getElement());
            }
        }
    }


    /**
     * Stop editing the currently selected item.
     */
    public void stopEditing()
    {
        returnItemToNonEditableState();
        currentSelectedItem = null;
    }

    /**
     * Supply an editor for this tree. Its matchesItem() method determines
     * for which nodes this editor will be displayed.
     * @param editor the editor to be added
     */
    public void addItemEditor(ItemEditor editor)
    {
        availableItemEditors.add(editor);
    }

    /**
     * Remove a previously-added editor from this tree. Editors
     * that are currently visible are not immediately affected.
     * @param editor the editor to be removed
     */
    public void removeItemEditor(ItemEditor editor)
    {
        availableItemEditors.remove(editor);
    }

    /**
     * Classes that implement this interface can be added to the
     * tree and will then be displayed in-line with the selected
     * item, whenever the editor's matchesItem(item) method returns
     * true for the tree's currently selected item.
     *
     * @see DefaultItemEditor an implementation that displays a clickable icon for a given edit action
     */
    public interface ItemEditor
    {
        /**
         *
         * @return a Widget that will be displayed in-line with the selected tree item
         */
        public Widget getWidget();

        /**
         * Whether this editor's widget should be displayed for the specified item
         * @param item  the selected tree item
         * @return  true if this editor is appropriate for the specified item
         */
        public boolean matchesItem(TreeItem item);
        public void startEditing(TreeItem item);
        public void stopEditing();
    }
}

